#include "ConnectionPool.h"

#include <chrono>
#include <functional>
#include <iostream>
#include <thread>

ConnectionPool::ConnectionPool(const std::string &_dbname,
                               const std::string &_user,
                               const std::string &_password,
                               const std::string &_host,
                               const std::string &_port)
    : dbname(_dbname),
      user(_user),
      password(_password),
      host(_host),
      port(_port),
      connect_url("dbname=" + dbname + " user=" + user + " password=" +
                  password + " hostaddr=" + host + " port=" + port) {
  //创建初始连接
  for (size_t i = 0; i < initSize; ++i) {
    Connection *p = new Connection(connect_url);
    p->refreshAliveTime();
    connectionQueue.push(p);
    ++connectionCnt;
  }

  //启动连接生产者线程
  std::thread producer(std::bind(&ConnectionPool::produceConnection, this));
  producer.detach();  //线程分离

  //连接回收线程
  std::thread scanner(std::bind(&ConnectionPool::scanConnection, this));
  scanner.detach();
}

ConnectionPool &ConnectionPool::getConnectionPool(const std::string &dbname,
                                                  const std::string &user,
                                                  const std::string &password,
                                                  const std::string &host,
                                                  const std::string &port) {
  static ConnectionPool pool(dbname, user, password, host, port);
  return pool;
}

void ConnectionPool::produceConnection() {
  while (true) {
    std::unique_lock<std::mutex> lk(queueMutex);
    //到队列空时创建新连接
    cv.wait(lk, [this] { return this->connectionQueue.empty(); });
    //到达上限前可以继续创建
    if (connectionCnt < maxSize) {
      Connection *p = new Connection(connect_url);
      p->refreshAliveTime();
      connectionQueue.push(p);
      ++connectionCnt;
    }
    //通知等待连接使用的消费者线程
    cv.notify_all();
    //到这里自动释放锁
  }
}

std::shared_ptr<Connection> ConnectionPool::getConnection() {
  std::unique_lock<std::mutex> lk(queueMutex);
  if (connectionQueue.empty()) {
    //当队列为空，需要等待（超时时间之内）
    if (std::cv_status::timeout ==
        cv.wait_for(lk, std::chrono::milliseconds(connectionTimeout))) {
      if (connectionQueue.empty()) {
        //超时，且没有连接可用返回空指针
        std::cerr << "connection timeout" << std::endl;
        return nullptr;
      }
    }
  }

  //给用户使用的connection智能指针，需要修改默认的删除器，不是直接delete掉，而是
  //返还给连接池
  std::shared_ptr<Connection> pConn(
      connectionQueue.front(), [this](Connection *p) {
        //注意线程安全
        std::lock_guard<std::mutex> lk(this->queueMutex);
        p->refreshAliveTime();
        this->connectionQueue.push(p);
      });
  connectionQueue.pop();
  //当拿的是最后一个连接，通知生产者生产新连接
  if (connectionQueue.empty()) cv.notify_all();

  return pConn;
}

void ConnectionPool::scanConnection() {
  while (true) {
    //定时回收
    std::this_thread::sleep_for(std::chrono::seconds(maxIdleTime));

    //从头扫描队列，将超过maxIdleTime的线程回收
    std::lock_guard<std::mutex> lk(queueMutex);
    while (connectionCnt > initSize) {
      auto p = connectionQueue.front();
      //对头的连接没有超时，后面的一定不会超时
      if (p->getAliveTime() < maxIdleTime * 1000) break;
      connectionQueue.pop();
      --connectionCnt;
      delete p;
    }
    //到这里释放锁
  }
}